// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/cert/ct_objects_extractor.h"

#include "base/files/file_path.h"
#include "net/cert/ct_log_verifier.h"
#include "net/cert/ct_serialization.h"
#include "net/cert/signed_certificate_timestamp.h"
#include "net/cert/x509_certificate.h"
#include "net/test/cert_test_util.h"
#include "net/test/ct_test_util.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace ct {

    class CTObjectsExtractorTest : public ::testing::Test {
    public:
        void SetUp() override
        {
            precert_chain_ = CreateCertificateListFromFile(GetTestCertsDirectory(),
                "ct-test-embedded-cert.pem",
                X509Certificate::FORMAT_AUTO);
            ASSERT_EQ(2u, precert_chain_.size());

            std::string der_test_cert(ct::GetDerEncodedX509Cert());
            test_cert_ = X509Certificate::CreateFromBytes(der_test_cert.data(),
                der_test_cert.length());

            log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog",
                "https://ct.example.com");
            ASSERT_TRUE(log_);
        }

        void ExtractEmbeddedSCT(scoped_refptr<X509Certificate> cert,
            scoped_refptr<SignedCertificateTimestamp>* sct)
        {
            std::string sct_list;
            EXPECT_TRUE(ExtractEmbeddedSCTList(cert->os_cert_handle(), &sct_list));

            std::vector<base::StringPiece> parsed_scts;
            base::StringPiece sct_list_sp(sct_list);
            // Make sure the SCT list can be decoded properly
            EXPECT_TRUE(DecodeSCTList(&sct_list_sp, &parsed_scts));

            EXPECT_TRUE(DecodeSignedCertificateTimestamp(&parsed_scts[0], sct));
        }

    protected:
        CertificateList precert_chain_;
        scoped_refptr<X509Certificate> test_cert_;
        scoped_refptr<const CTLogVerifier> log_;
    };

    // Test that an SCT can be extracted and the extracted SCT contains the
    // expected data.
    TEST_F(CTObjectsExtractorTest, ExtractEmbeddedSCT)
    {
        scoped_refptr<ct::SignedCertificateTimestamp> sct(
            new ct::SignedCertificateTimestamp());
        ExtractEmbeddedSCT(precert_chain_[0], &sct);

        EXPECT_EQ(sct->version, SignedCertificateTimestamp::V1);
        EXPECT_EQ(ct::GetTestPublicKeyId(), sct->log_id);

        base::Time expected_timestamp = base::Time::UnixEpoch() + base::TimeDelta::FromMilliseconds(1365181456275);
        EXPECT_EQ(expected_timestamp, sct->timestamp);
    }

    TEST_F(CTObjectsExtractorTest, ExtractPrecert)
    {
        LogEntry entry;
        ASSERT_TRUE(GetPrecertLogEntry(precert_chain_[0]->os_cert_handle(),
            precert_chain_[1]->os_cert_handle(),
            &entry));

        ASSERT_EQ(ct::LogEntry::LOG_ENTRY_TYPE_PRECERT, entry.type);
        // Should have empty leaf cert for this log entry type.
        ASSERT_TRUE(entry.leaf_certificate.empty());
        // Compare hash values of issuer spki.
        SHA256HashValue expected_issuer_key_hash;
        memcpy(expected_issuer_key_hash.data, GetDefaultIssuerKeyHash().data(), 32);
        ASSERT_EQ(expected_issuer_key_hash, entry.issuer_key_hash);
    }

    TEST_F(CTObjectsExtractorTest, ExtractOrdinaryX509Cert)
    {
        LogEntry entry;
        ASSERT_TRUE(GetX509LogEntry(test_cert_->os_cert_handle(), &entry));

        ASSERT_EQ(ct::LogEntry::LOG_ENTRY_TYPE_X509, entry.type);
        // Should have empty tbs_certificate for this log entry type.
        ASSERT_TRUE(entry.tbs_certificate.empty());
        // Length of leaf_certificate should be 718, see the CT Serialization tests.
        ASSERT_EQ(718U, entry.leaf_certificate.size());
    }

    // Test that the embedded SCT verifies
    TEST_F(CTObjectsExtractorTest, ExtractedSCTVerifies)
    {
        scoped_refptr<ct::SignedCertificateTimestamp> sct(
            new ct::SignedCertificateTimestamp());
        ExtractEmbeddedSCT(precert_chain_[0], &sct);

        LogEntry entry;
        ASSERT_TRUE(GetPrecertLogEntry(precert_chain_[0]->os_cert_handle(),
            precert_chain_[1]->os_cert_handle(),
            &entry));

        EXPECT_TRUE(log_->Verify(entry, *sct.get()));
    }

    // Test that an externally-provided SCT verifies over the LogEntry
    // of a regular X.509 Certificate
    TEST_F(CTObjectsExtractorTest, ComplementarySCTVerifies)
    {
        scoped_refptr<ct::SignedCertificateTimestamp> sct(
            new ct::SignedCertificateTimestamp());
        GetX509CertSCT(&sct);

        LogEntry entry;
        ASSERT_TRUE(GetX509LogEntry(test_cert_->os_cert_handle(), &entry));

        EXPECT_TRUE(log_->Verify(entry, *sct.get()));
    }

    // Test that the extractor can parse OCSP responses.
    TEST_F(CTObjectsExtractorTest, ExtractSCTListFromOCSPResponse)
    {
        std::string der_subject_cert(ct::GetDerEncodedFakeOCSPResponseCert());
        scoped_refptr<X509Certificate> subject_cert = X509Certificate::CreateFromBytes(der_subject_cert.data(),
            der_subject_cert.length());
        std::string der_issuer_cert(ct::GetDerEncodedFakeOCSPResponseIssuerCert());
        scoped_refptr<X509Certificate> issuer_cert = X509Certificate::CreateFromBytes(der_issuer_cert.data(),
            der_issuer_cert.length());

        std::string fake_sct_list = ct::GetFakeOCSPExtensionValue();
        ASSERT_FALSE(fake_sct_list.empty());
        std::string ocsp_response = ct::GetDerEncodedFakeOCSPResponse();

        std::string extracted_sct_list;
        EXPECT_TRUE(ct::ExtractSCTListFromOCSPResponse(
            issuer_cert->os_cert_handle(), subject_cert->serial_number(),
            ocsp_response, &extracted_sct_list));
        EXPECT_EQ(extracted_sct_list, fake_sct_list);
    }

    // Test that the extractor honours serial number.
    TEST_F(CTObjectsExtractorTest, ExtractSCTListFromOCSPResponseMatchesSerial)
    {
        std::string der_issuer_cert(ct::GetDerEncodedFakeOCSPResponseIssuerCert());
        scoped_refptr<X509Certificate> issuer_cert = X509Certificate::CreateFromBytes(der_issuer_cert.data(),
            der_issuer_cert.length());

        std::string ocsp_response = ct::GetDerEncodedFakeOCSPResponse();

        std::string extracted_sct_list;
        EXPECT_FALSE(ct::ExtractSCTListFromOCSPResponse(
            issuer_cert->os_cert_handle(), test_cert_->serial_number(),
            ocsp_response, &extracted_sct_list));
    }

    // Test that the extractor honours issuer ID.
    TEST_F(CTObjectsExtractorTest, ExtractSCTListFromOCSPResponseMatchesIssuer)
    {
        std::string der_subject_cert(ct::GetDerEncodedFakeOCSPResponseCert());
        scoped_refptr<X509Certificate> subject_cert = X509Certificate::CreateFromBytes(der_subject_cert.data(),
            der_subject_cert.length());

        std::string ocsp_response = ct::GetDerEncodedFakeOCSPResponse();

        std::string extracted_sct_list;
        // Use test_cert_ for issuer - it is not the correct issuer of |subject_cert|.
        EXPECT_FALSE(ct::ExtractSCTListFromOCSPResponse(
            test_cert_->os_cert_handle(), subject_cert->serial_number(),
            ocsp_response, &extracted_sct_list));
    }

} // namespace ct

} // namespace net
